package com.beta.cat.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT（JSON Web Token）是为了在网络应用环境中传递声明而执行的一种基于JSON的开放标准（RFC 7519）。
 * 基于HMAC算法或RSA的公私秘钥对进行签名。
 * 组成：头Header、负载 Payload、签名 Signature
 */
public class JwtUtil {
    /**
     * 私钥
     */
    private static final String TOKEN_SECRET = "!Q@W#E$R";
    /**
     * 发行人
     */
    private static String ISSUER = "user_kevin";

    /**
     * token 失效时间
     */
    private static final String EXP_DATE = "expDate";
    /**
     * token 失效时长
     */
    private static final String EXP_DURATION = "expDuration";

    /**
     * 生成签名 Token
     *
     * @param claims
     * @param expiresDuration
     * @return
     */
    public static String encrypt(Map<String, String> claims, long expiresDuration) {
        return encrypt(TOKEN_SECRET, ISSUER, claims, expiresDuration);
    }

    /**
     * 生成签名 Token
     *
     * @param secret
     * @param issuer
     * @param claims
     * @param expiresDuration
     * @return
     */
    public static String encrypt(String secret, String issuer, Map<String, String> claims, long expiresDuration) {
        try {
            //使用 HMAC256 进行加密，私钥和加密算法（默认）
            Algorithm algorithm = Algorithm.HMAC256(secret);
            Map<String, Object> header = new HashMap<>(2);
            header.put("alg", "HS256");
            header.put("typ", "JWT");

            Date expiresDate = new Date(System.currentTimeMillis() + expiresDuration);
            //Payload：发行人、过期时间点
            /**
             * iss (issuer)：签发人
             * exp (expiration time)：过期时间，必须要大于签发时间
             * sub (subject)：主题
             * aud (audience)：受众
             * nbf (Not Before)：生效时间
             * iat (Issued At)：签发时间
             * jti (JWT ID)：编号，JWT 的唯一身份标识，主要用来作为一次性 token，从而回避重放攻击。
             */
            JWTCreator.Builder builder = JWT.create()
                    .withHeader(header)
                    .withIssuer(issuer)
                    .withIssuedAt(new Date())
                    .withExpiresAt(expiresDate);
            //传入参数
            claims.forEach((key, value) -> builder.withClaim(key, value));

            //签名加密，返回token字符串
            return builder.sign(algorithm);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 验证签名 Token
     *
     * @param token
     * @return
     */
    public static boolean verify(String token) {
        return verify(TOKEN_SECRET, token);
    }

    /**
     * 验证签名 Token
     *
     * @param secret
     * @param token
     * @return
     */
    public static boolean verify(String secret, String token) {
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        try {
            //使用 HMAC256 进行加密，私钥和加密算法（默认）
            Algorithm algorithm = Algorithm.HMAC256(secret);
            //解密
            JWTVerifier verifier = JWT.require(algorithm).build();
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 解密签名 Token
     *
     * @param token
     * @return
     */
    public static Map<String, String> decrypt(String token) {
        return decrypt(TOKEN_SECRET, ISSUER, token, null);
    }

    /**
     * 解密签名 Token
     *
     * @param token
     * @param tokenTimeInfo
     * @return
     */
    public static Map<String, String> decrypt(String token, Map tokenTimeInfo) {
        return decrypt(TOKEN_SECRET, ISSUER, token, tokenTimeInfo);
    }

    /**
     * 解密签名 Token
     *
     * @param secret
     * @param issuer
     * @param token
     * @param tokenTimeInfo
     * @return
     */
    public static Map<String, String> decrypt(String secret, String issuer, String token, Map tokenTimeInfo) {
        Map<String, String> resultMap = new HashMap<>();
        try {
            //使用 HMAC256 进行加密，私钥和加密算法（默认）
            Algorithm algorithm = Algorithm.HMAC256(secret);
            //解密
            JWTVerifier verifier = JWT.require(algorithm).withIssuer(issuer).build();
            DecodedJWT jwt = verifier.verify(token);
            if (null != tokenTimeInfo) {
                //失效时间
                tokenTimeInfo.put(EXP_DATE, DateUtil.date2Str(jwt.getExpiresAt()));
                //失效时长
                tokenTimeInfo.put(EXP_DURATION, jwt.getExpiresAt().getTime() - jwt.getIssuedAt().getTime());
            }
            Map<String, Claim> map = jwt.getClaims();
            map.forEach((k, v) -> resultMap.put(k, v.asString()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultMap;
    }

    /**
     * 刷新令牌
     *
     * @param token
     * @return
     */
    public static String refreshToken(String token) {
        String refreshedToken = token;
        try {
            Map tokenTimeInfo = new HashMap();
            Map map = decrypt(token, tokenTimeInfo);
            String expDate = tokenTimeInfo.get(EXP_DATE).toString();
            long expDateMillis = DateUtil.str2Date(expDate, DateUtil.YYYY_MM_DD_HH_MM_SS).getTime();
            long expiresDuration = Long.parseLong(tokenTimeInfo.get(EXP_DURATION).toString());
            long currentTimeMillis = System.currentTimeMillis();

            //未过期，且有效期过了一半以上才更新token
            if (expDateMillis > currentTimeMillis && (expDateMillis - currentTimeMillis) > expiresDuration / 2) {
                refreshedToken = encrypt(map, expiresDuration);
            }
        } catch (Exception e) {
            refreshedToken = token;
        }
        return refreshedToken;
    }
}
